#include "openmove_Prefix.h"
#include "OgreEngine.h"
#include "SceneBase.h"
#include "NetBase.h"
#include "SyncLib/SyncManager.h"
#include "NetClient.h"
#include "NetServer.h"

#define DEFAULT_RESOURCE_FILENAME "resources.cfg"
#define DEFAULT_APPLICATION_NAME "nncorsino :: openmove"
#define DEFAULT_VIEWPORT_ZORDER 0
#define DEFAULT_CAMERA_NAME "DefaultCamera"

COgreEngine :: COgreEngine(void) :
m_ResourceFilename(DEFAULT_RESOURCE_FILENAME),
m_ApplicationName(DEFAULT_APPLICATION_NAME),
m_pCurrentRenderWindow(NULL),
m_pCurrentSceneManager(NULL),
m_HasDebugLight(true),
m_pEventProcessor(NULL),
m_pViewPort(NULL),
m_pCurrentScene(NULL),
m_RemoveListeners(false),
m_AddListeners(false),
m_LastTime(0),
m_MsToWait(25),
m_GUIRenderer(NULL),
m_GUISystem(NULL),
m_statsOn(false)
{	
}

COgreEngine :: ~COgreEngine(void)
{
}

void COgreEngine :: Create(void)
{
    ConfigFile cf;
    cf.load(m_ResourceFilename);

    // Go through all sections & settings in the file
    ConfigFile::SectionIterator seci = cf.getSectionIterator();

    String secName, typeName, archName;
    while (seci.hasMoreElements())
    {
        secName = seci.peekNextKey();
        ConfigFile::SettingsMultiMap *settings = seci.getNext();
        ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i)
        {
            typeName = i->first;
            archName = i->second;
            ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
        }
    }

	if (true != m_Root.restoreConfig())
	{
		m_Root.showConfigDialog(); 
		m_Root.saveConfig();
	}
	m_pCurrentRenderWindow = m_Root.initialise(true, m_ApplicationName);

	ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

	m_pCurrentSceneManager = m_Root.createSceneManager(ST_GENERIC); 

	Animation::setDefaultInterpolationMode(Animation::IM_LINEAR);
	Animation::setDefaultRotationInterpolationMode(Animation::RIM_LINEAR);

	m_pEventProcessor = new EventProcessor(); 
	m_pEventProcessor->initialise(m_Root.getAutoCreatedWindow()); 
	m_pEventProcessor->startProcessingEvents(); 

	m_pDefaultCamera = m_pCurrentSceneManager->createCamera(DEFAULT_CAMERA_NAME);
	m_pViewPort = m_Root.getAutoCreatedWindow()->addViewport(m_pDefaultCamera, DEFAULT_VIEWPORT_ZORDER);


	m_pCurrentSceneManager->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE);
	m_pCurrentSceneManager->setShadowFarDistance(1000);
	m_pCurrentSceneManager->setShadowTextureCount(1);
	m_pCurrentSceneManager->setShadowTextureSize(512);
	m_pCurrentSceneManager->setShadowTextureSelfShadow(false);
	m_pCurrentSceneManager->setShowDebugShadows(false);
	m_pCurrentSceneManager->showBoundingBoxes(false);
	m_pCurrentSceneManager->setShadowColour(ColourValue(0.5, 0.5, 0.5, 0.5));
	m_Root.addFrameListener(this);

	m_GUIRenderer = new CEGUI::OgreCEGUIRenderer(m_pCurrentRenderWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, m_pCurrentSceneManager);
	m_GUISystem = new CEGUI::System(m_GUIRenderer);
	CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLook.scheme");
	m_GUISystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");
	m_GUISystem->setDefaultFont((CEGUI::utf8*)"Tahoma-8");

	LogManager::getSingleton().setLogDetail(LL_LOW);
}

void COgreEngine :: AddDefaultLight(void)
{
	m_pCurrentSceneManager->setAmbientLight(ColourValue(0.2, 0.2, 0.2));

	struct LightDecl
	{
		LightDecl(Vector3 Value1, ColourValue Value2, ColourValue Value3, String const &Name) : position(Value1), diffuse(Value2), specular(Value3), name(Name) {};
		Vector3 position;
		ColourValue diffuse;
		ColourValue specular;
		String name;
	};

	int const NbLight = 2;
	LightDecl pLights[NbLight] = 
	{
		LightDecl(Vector3(0.0f, 235.911f, -154.772f), ColourValue(0.3f, 0.3f, 0.3f), ColourValue(1.0f, 1.0f, 1.0f), "pointLight1"),
		LightDecl(Vector3(-164.673f, 392.404f, -170.636f), ColourValue(0.6f, 0.6f, 0.6f), ColourValue(1, 1, 1), "pointLight4"),
	};

	for (int i = 0; i < NbLight; ++i)
	{
		String lightName = pLights[i].name;
		Light * light = m_pCurrentSceneManager->createLight(lightName);
		light->setType(Light::LT_POINT);
		light->setPosition(pLights[i].position);
		light->setDiffuseColour(pLights[i].diffuse);
		light->setSpecularColour(pLights[i].specular);

		if (m_HasDebugLight)
		{
			SceneNode *pLightNode = m_pCurrentSceneManager->getRootSceneNode()->createChildSceneNode();
			String lightEntityName = "light" + StringConverter::toString(i);
			Entity *pLightEntity = m_pCurrentSceneManager->createEntity(lightEntityName, "directionalLight.mesh");
			pLightNode->attachObject(pLightEntity);
			Vector3 rot;
			rot.x = light->getDirection().dotProduct(Vector3(1, 0, 0));
			rot.y = light->getDirection().dotProduct(Vector3(0, 1, 0));
			rot.z = light->getDirection().dotProduct(Vector3(0, 0, 1));
			pLightNode->rotate(Vector3(1, 0, 0), static_cast<Radian>(rot.x));
			pLightNode->rotate(Vector3(0, 1, 0), static_cast<Radian>(rot.y));
			pLightNode->rotate(Vector3(0, 0, 1), static_cast<Radian>(rot.z));

			pLightNode->translate(light->getPosition());
			pLightNode->scale(2, 2, 2);
		}
	}
}

COgreEngine::TScenes::iterator COgreEngine :: findScene(int const id)
{
	for (TScenes::iterator It = m_Scenes.begin(); It != m_Scenes.end(); ++It)
	{
		if ((*It)->m_ScenePos == id)
		{
			return It;
		}
	}

	return m_Scenes.end();
}

void COgreEngine :: startScene(int sceneId)
{
	TScenes::iterator It = findScene(sceneId);
	if (It == m_Scenes.end())
	{
		return;
	}
	if (m_pCurrentScene == (*It))
	{
		m_pCurrentScene->Reset();
		return;
	}
	if (NULL != m_pCurrentScene)
	{
		m_pLastScene = m_pCurrentScene;
		m_pCurrentScene->Detach();
		m_RemoveListeners = true;
	}
	m_pCurrentScene = (*It);
	m_pCurrentScene->Attach();
	m_pCurrentScene->Reset();
	m_AddListeners = true;
}

void COgreEngine :: startNextScene(void)
{
	TScenes::iterator It;
	if (NULL != m_pCurrentScene)
	{
		It = std::find(m_Scenes.begin(), m_Scenes.end(), m_pCurrentScene);
		assert(It != m_Scenes.end());
		++It;
		if (It == m_Scenes.end())
		{
			It = m_Scenes.begin();
		}

		m_pLastScene = m_pCurrentScene;
		m_pCurrentScene->Detach();
		m_RemoveListeners = true;
	}
	else
	{
		It = m_Scenes.begin();
	}
	(*It)->Attach();
	(*It)->Reset();
	m_pCurrentScene = (*It);
	m_AddListeners = true;
}

void COgreEngine :: startPreviousScene(void)
{
	TScenes::reverse_iterator It;
	if (NULL != m_pCurrentScene)
	{
		It = std::find(m_Scenes.rbegin(), m_Scenes.rend(), m_pCurrentScene);
		assert(It != m_Scenes.rend());
		++It;
		if (It == m_Scenes.rend())
		{
			It = m_Scenes.rbegin();
		}
		m_pLastScene = m_pCurrentScene;
		m_pCurrentScene->Detach();
		m_RemoveListeners = true;
	}
	else
	{
		It = m_Scenes.rbegin();
	}
	(*It)->Attach();
	(*It)->Reset();
	m_pCurrentScene = (*It);
	m_AddListeners = true;
}

void COgreEngine :: addScene(CSceneBase *pScene)
{
	m_Scenes.push_back(pScene);
}

bool COgreEngine :: frameStarted(const FrameEvent& evt)
{
#if 0
	while (m_Timer.getMilliseconds() - m_LastTime < m_MsToWait && CSyncManager::getSingleton().m_IsServer)
	{
		Sleep(0);
	}
#endif
	m_LastTime = m_Timer.getMilliseconds();


	return (true);
}

bool COgreEngine :: frameEnded(const FrameEvent& evt)
{
#ifdef __USE_HAWKTHREAD__
	if (CSyncManager::getSingleton().m_IsServer)
	{
		htMutexLock(&CNetServer::ms_pSingleton->m_QueueReadMutex);
	}
	else
	{
		htMutexLock(&CNetClient::ms_pSingleton->m_QueueReadMutex);
	}
#endif

	for (TTasks::iterator it = m_Tasks.begin(); it != m_Tasks.end(); ++it)
	{
		switch (it->m_Id)
		{
			case CMD_SETSCENE:
			{
				startScene(it->m_SceneId);
				break;
			}
			case CMD_NEXTSCENE:
			{
				startNextScene();
				break;
			}
			case CMD_PREVSCENE:
			{
				startPreviousScene();
				break;
			}
			default:
			{
				break;
			}
		}
	}
	m_Tasks.clear();

	if (m_RemoveListeners)
	{
		m_pLastScene->RemoveListeners();
		m_RemoveListeners = false;
	}

	if (m_AddListeners)
	{
		m_pCurrentScene->AddListeners();
		m_AddListeners = false;
	}

#ifdef __USE_HAWKTHREAD__
	if (CSyncManager::getSingleton().m_IsServer)
	{
		htMutexUnlock(&CNetServer::ms_pSingleton->m_QueueReadMutex);
	}
	else
	{
		htMutexUnlock(&CNetClient::ms_pSingleton->m_QueueReadMutex);
	}
#endif

	return (true);
}

void COgreEngine :: setDebugText(String const &text)
{
	if (m_statsOn)
	{
	if ('\n' == text[text.length() - 1])
	{
		m_pCurrentRenderWindow->setDebugText(m_pCurrentRenderWindow->getDebugText() + text);
	}
	else
	{
		m_pCurrentRenderWindow->setDebugText(m_pCurrentRenderWindow->getDebugText() + text + "\n");
	}
	}
}

CEGUI::MouseButton COgreEngine :: convertOgreButtonToCegui(int buttonID)
{
   switch (buttonID)
   {
   case MouseEvent::BUTTON0_MASK:
       return CEGUI::LeftButton;
   case MouseEvent::BUTTON1_MASK:
       return CEGUI::RightButton;
   case MouseEvent::BUTTON2_MASK:
       return CEGUI::MiddleButton;
   case MouseEvent::BUTTON3_MASK:
       return CEGUI::X1Button;
   default:
       return CEGUI::LeftButton;
   }
}
